package jvm;

import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPool extends ThreadPoolExecutor {

    /**
     * 改进版的线程池,非 核心线程用完+任务队列满时 才启用扩展线程
     * 在核心线程池用完时,立即启动扩展线程,当扩展线程用完时在放进队列中
     * 不用重写线程池代码,只需重写队列的offer代码,线程池代码会自己处理;
     * <p>
     * 1.重写offer代码,[在进入队列检测线程的使用量,如果没达到最大线程数],那么就直接抛出异常,抛出数异常后线程池就会扩大线程量,然后将此任务交给最大线程
     * 2.如果最大线程满了,加入队列又异常,那么势必会触发拒绝策略,我们在自编写一个拒绝策略,然后将任务放入队列中
     */

    private MyBlockQueue myBlockQueue;

    public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int queueSize) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingQueue<>());
        Class<?> superclass = this.getClass().getSuperclass();
        try {
            Field workQueue = superclass.getDeclaredField("workQueue");//属性
            workQueue.setAccessible(true);//权限
            MyBlockQueue myBlockQueue = new MyBlockQueue(queueSize);
            this.myBlockQueue = myBlockQueue;
            workQueue.set(this, myBlockQueue);//给当前对象属性设置新值
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

        this.setRejectedExecutionHandler((runnable, threadPoolExecutor) -> {
            //当线程return false时,并且线程数量达到最大时,会进入此拒绝策略,此时再放入队列中
            //注: 当前进来的线程是调用者线程
            boolean flag = this.myBlockQueue.myOffer(runnable);
            if (!flag) {
                //如果队列满了,那么就抛出异常
                throw new RejectedExecutionException("Task " + runnable.toString() +
                        " rejected from " +
                        threadPoolExecutor.toString());
            }
        });

    }

    class MyBlockQueue extends LinkedBlockingQueue<Runnable> {

        //最大线程数量
        int maxThreadCount = MyThreadPool.this.getMaximumPoolSize();

        //核心线程数量
//        int coreThreadCount=getCorePoolSize();

        //当前的线程数量
//        int threadCount=getPoolSize();

        //当前正在执行任务的线程数量
//        int activeThreadCount=getActiveCount();

        public MyBlockQueue(int capacity) {
            super(capacity);
        }

        @Override
        public boolean offer(Runnable runnable) {

            //当前线程数量未达到最大线程数量,就return false 已满 信号,让线程池创建新的线程去执行
            if (MyThreadPool.this.getPoolSize() < this.maxThreadCount) {
                return false;
            }

            return super.offer(runnable);
        }

        public boolean myOffer(Runnable runnable) {
            //直接放入任务队列
            return super.offer(runnable);
        }

    }

    public static void main(String[] args) {

        Runnable runnable = () -> {
            Random random = new Random();
            System.out.println("休眠开始");
            try {
                Thread.sleep(random.nextInt(5) * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("休眠结束");
        };

        MyThreadPool myThreadPool = new MyThreadPool(3, 10, 30, TimeUnit.SECONDS, 5);

        for (int i = 0; i < 15; i++) {
            //正如预测: [15不会抛出异常,16个任务就会抛出异常]
            myThreadPool.execute(runnable);
        }

        System.out.println(myThreadPool.getActiveCount());
        System.out.println(myThreadPool.getPoolSize());


        ExecutorService executorService = Executors.newFixedThreadPool(5);

        executorService.submit(() -> "握手callable任务结果");

        //附带一个提升获取Future,来提升 获取submit任务结果 效率的工具类 ExecutorCompletionService
        CompletionService completionService = new ExecutorCompletionService(executorService);

        try {
            completionService.take();//获取执行完成的future任务
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}
